package com.yan.utils;

/**
 * Created by yanl_ on 2018/4/24.
 *
 * https://blog.csdn.net/JAZZSOLDIER/article/details/77987480
 */
public class DataQueue {

    Queue q = null;

    public DataQueue() {

    }


    public class Queue {
        byte[] data = null;
        int read;
        int write;
        int size;
        int space;
    }

    Object mSemaphore = new Object();

    DataQueue(int size) {
        q = new Queue();
        Queue_Init(q, size);
    }

    /**
     * 返回当前队列可用数据量
     *
     * @param q 目标队列
     */
    int Avail(Queue q) {
        return q.size - q.space;
    }

    /**
     * 初始化队列
     *
     * @param q    目标队列
     * @param size 队列分配内存大小
     */
    void Queue_Init(Queue q, int size) {
        synchronized (mSemaphore) {
            q.data = new byte[size];
            q.read = 0;
            q.write = 0;
            q.size = size;
            q.space = size;
        }
    }

    /**
     * 销毁队列
     *
     * @param q 目标队列
     */
    void Queue_Destroy(Queue q) {
        synchronized (mSemaphore) {
            q.read = q.write = 0;
            q.space = q.size;
        }
    }

    /**
     * 判断当前队列是否为空
     *
     * @param q 目标队列
     * @return true表示队列为空<br>false表示队列不为空
     */
    boolean Queue_Empty(Queue q) {
        return (q.space == q.size);
    }

    /**
     * 判断当前队列是否已满
     *
     * @param q 目标队列
     * @return true表示队列已满<br>false表示队列未满
     */
    boolean Queue_Full(Queue q) {
        return (q.space == 0);
    }

    /**
     * 写一个byte到目标队列
     *
     * @param q   目标队列
     * @param val 写入的byte值
     * @return true表示写入成功<br>false表示写入失败
     */
    boolean AddQueue(Queue q, byte val) {
        if (!Queue_Full(q)) {
            q.data[q.write] = val;
            q.write = (q.write + 1) % q.size;
            q.space--;
            return true;
        }
        return false;
    }

    /**
     * 从队列中读取一个字节
     *
     * @param q    目标队列
     * @param data 读取的字节
     * @return true表示读取成功<br>false表示读取失败
     */
    boolean DelQueue(Queue q, Byte data) {
        if (!Queue_Empty(q)) {
            data = q.data[q.read];

            q.read = (q.read + 1) % q.size;
            q.space++;
            return true;
        }
        return false;
    }

    /**
     * 批量写入长度为len的字节到队列
     *
     * @param q    目标队列
     * @param data 写入的byte数组
     * @param len  写入的数组长度
     * @return 成功写入的字节数量
     */
    int WriteQueue(Queue q, byte[] data, int len) {
        int ret = 0;
        int rest = q.size - q.write;

        synchronized (mSemaphore) {
            if (!Queue_Full(q)) {
                if (q.space >= len) {
                    ret = len;
                    if (rest >= len) {
                        System.arraycopy(data, 0, q.data, q.write, len);
                        q.write = (q.write + len) % q.size;
                        q.space -= len;
                    } else {
                        System.arraycopy(data, 0, q.data, q.write, rest);
                        q.write = 0;
                        System.arraycopy(data, rest, q.data, 0, len - rest);
                        q.write = len - rest;
                        q.space -= len;
                    }
                } else {
                    ret = q.space;
                    if (rest >= q.space) {
                        System.arraycopy(data, 0, q.data, q.write, q.space);
                        q.write = (q.write + q.space) % q.size;
                        q.space = 0;
                    } else {
                        System.arraycopy(data, 0, q.data, q.write, rest);
                        q.write = 0;
                        System.arraycopy(data, rest, q.data, 0, q.space - rest);
                        q.write = q.space - rest;
                        q.space = 0;
                    }
                }
            }
            return ret;
        }
    }

    /**
     * 从队列中恢复长度len个字节的数据
     *
     * @param q   目标队列
     * @param len 要恢复的长度
     * @return 成功恢复的字节数
     */
    int RecoverReadQueue(Queue q, int len) {
        int ret = 0;
        int rest = q.read;

        synchronized (mSemaphore) {

            if (q.space >= len)
                ret = len;
            else
                ret = q.space;

            if (rest >= ret) {
                q.read -= ret;
            } else {
                q.read = q.size - (ret - rest);
            }
            q.space -= ret;

            return ret;
        }
    }

    /**
     * 从队列中读取len个字节数据到data数组中
     *
     * @param q     目标队列
     * @param data  用于存放数据的目标数组
     * @param start 拷贝至目标数组的起始位置
     * @param len   读取的长度
     * @return 成功读取的字节数
     */
    int ReadQueue(Queue q, byte[] data, int start, int len) {
        int rest = q.size - q.read;
        int ret = 0;

        synchronized (mSemaphore) {
            if (!Queue_Empty(q)) {
                if (Avail(q) >= len) {
                    ret = len;
                    if (rest >= len) {
                        System.arraycopy(q.data, q.read, data, start, len);
                        q.read = (q.read + len) % q.size;
                        q.space += len;
                    } else {
                        System.arraycopy(q.data, q.read, data, start, rest);
                        q.read = 0;
                        System.arraycopy(q.data, 0, data, start + rest, len - rest);
                        q.read = len - rest;
                        q.space += len;
                    }
                    return len;
                } else {
                    ret = Avail(q);
                    if (rest >= Avail(q)) {
                        System.arraycopy(q.data, q.read, data, start, Avail(q));
                        q.read = (q.read + Avail(q)) % q.size;
                        q.space = q.size;
                    } else {
                        System.arraycopy(q.data, q.read, data, start, rest);
                        q.read = 0;
                        System.arraycopy(q.data, 0, data, start + rest, Avail(q) - rest);
                        q.read = Avail(q) - rest;
                        q.space = q.size;
                    }
                }
            }
            return ret;
        }
    }


    void TestDataQueue() {
        DataQueue dataq = new DataQueue(100);
        byte[] a1 = {1, 2, 3, 4, 5, 6};
        byte[] a2 = {7, 8, 9, 10};
        byte[] b = new byte[10];
        int nread = 0;
        dataq.WriteQueue(dataq.q, a1, a1.length);
        nread = dataq.ReadQueue(dataq.q, b, 0, 3);
        System.out.println("length of queue: " + dataq.Avail(dataq.q));
        for (int i = 0; i < nread; i++) {
            System.out.printf("byte[%d]: %d\n", i, b[i]);
        }
        dataq.WriteQueue(dataq.q, a2, a2.length);
        System.out.println("length of queue: " + dataq.Avail(dataq.q));
        nread = dataq.ReadQueue(dataq.q, b, 0, dataq.Avail(dataq.q));
        System.out.println("length of queue: " + dataq.Avail(dataq.q));
        for (int i = 0; i < nread; i++) {
            System.out.printf("byte[%d]: %d\n", i, b[i]);
        }
    }

    public static void main(String args[]) {
        DataQueue t = new DataQueue();
        t.TestDataQueue();
    }
}